"""
流媒体质量自适应服务
"""

import asyncio
import time
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass, field
from enum import Enum
import statistics
import threading

from ...core.exceptions import BusinessLogicError, ValidationError
from ...core.logging import get_logger
from ...core.config import settings

logger = get_logger(__name__)


class QualityLevel(Enum):
    """质量级别"""
    LOW = "low"           # 低质量
    MEDIUM = "medium"     # 中等质量
    HIGH = "high"         # 高质量
    ULTRA = "ultra"       # 超高质量
    AUTO = "auto"         # 自动


class NetworkCondition(Enum):
    """网络状况"""
    EXCELLENT = "excellent"  # 优秀
    GOOD = "good"           # 良好
    FAIR = "fair"           # 一般
    POOR = "poor"           # 较差
    VERY_POOR = "very_poor" # 很差


class AdaptationStrategy(Enum):
    """自适应策略"""
    CONSERVATIVE = "conservative"  # 保守策略：缓慢调整质量
    BALANCED = "balanced"          # 平衡策略：适中调整质量
    AGGRESSIVE = "aggressive"      # 激进策略：快速调整质量
    PRELOAD_FOCUSED = "preload_focused"  # 预加载优先策略


@dataclass
class QualityConfig:
    """质量配置"""
    level: QualityLevel
    bitrate: int          # kbps
    sample_rate: int       # Hz
    channels: int         # 声道数
    buffer_size: int       # 缓冲区大小
    format: str           # 音频格式
    file_size_ratio: float  # 文件大小比例（相对于最低质量）
    bandwidth_requirement: float  # 带宽需求 kbps
    
    def __post_init__(self):
        if not hasattr(self, 'cpu_usage_estimate'):
            # 估算CPU使用率（相对值）
            self.cpu_usage_estimate = {
                QualityLevel.LOW: 1.0,
                QualityLevel.MEDIUM: 2.0,
                QualityLevel.HIGH: 3.0,
                QualityLevel.ULTRA: 4.0
            }.get(self.level, 2.0)


@dataclass
class NetworkMetrics:
    """网络指标"""
    bandwidth_kbps: float = 0.0
    latency_ms: float = 0.0
    packet_loss_rate: float = 0.0
    jitter_ms: float = 0.0
    timestamp: float = field(default_factory=time.time)
    
    def get_condition(self) -> NetworkCondition:
        """获取网络状况"""
        # 综合评分算法
        bandwidth_score = min(1.0, self.bandwidth_kbps / 5000.0)  # 5Mbps为满分
        latency_score = max(0.0, 1.0 - (self.latency_ms - 20) / 180)  # 200ms为0分
        packet_loss_score = max(0.0, 1.0 - self.packet_loss_rate * 10)  # 10%丢包为0分
        jitter_score = max(0.0, 1.0 - (self.jitter_ms - 10) / 90)  # 100ms抖动为0分
        
        overall_score = (bandwidth_score * 0.4 + 
                        latency_score * 0.3 + 
                        packet_loss_score * 0.2 + 
                        jitter_score * 0.1)
        
        if overall_score >= 0.9:
            return NetworkCondition.EXCELLENT
        elif overall_score >= 0.7:
            return NetworkCondition.GOOD
        elif overall_score >= 0.5:
            return NetworkCondition.FAIR
        elif overall_score >= 0.3:
            return NetworkCondition.POOR
        else:
            return NetworkCondition.VERY_POOR


@dataclass
class PerformanceMetrics:
    """性能指标"""
    cpu_usage: float = 0.0
    memory_usage: float = 0.0
    buffer_health: float = 0.0
    underrun_count: int = 0
    rebuffer_count: int = 0
    average_wait_time: float = 0.0
    timestamp: float = field(default_factory=time.time)


@dataclass
class AdaptationHistory:
    """自适应历史"""
    timestamp: float
    old_quality: QualityLevel
    new_quality: QualityLevel
    network_condition: NetworkCondition
    performance_metrics: PerformanceMetrics
    reason: str
    success: bool


class AdaptiveQualityManager:
    """自适应质量管理器"""
    
    def __init__(self):
        # 质量配置
        self.quality_configs = {
            QualityLevel.LOW: QualityConfig(
                level=QualityLevel.LOW,
                bitrate=64,
                sample_rate=22050,
                channels=1,
                buffer_size=2048,
                format="mp3",
                file_size_ratio=0.5,
                bandwidth_requirement=80
            ),
            QualityLevel.MEDIUM: QualityConfig(
                level=QualityLevel.MEDIUM,
                bitrate=128,
                sample_rate=44100,
                channels=2,
                buffer_size=4096,
                format="mp3",
                file_size_ratio=1.0,
                bandwidth_requirement=160
            ),
            QualityLevel.HIGH: QualityConfig(
                level=QualityLevel.HIGH,
                bitrate=256,
                sample_rate=44100,
                channels=2,
                buffer_size=8192,
                format="mp3",
                file_size_ratio=2.0,
                bandwidth_requirement=320
            ),
            QualityLevel.ULTRA: QualityConfig(
                level=QualityLevel.ULTRA,
                bitrate=320,
                sample_rate=48000,
                channels=2,
                buffer_size=16384,
                format="flac",
                file_size_ratio=3.0,
                bandwidth_requirement=400
            )
        }
        
        # 会话管理
        self.sessions: Dict[str, 'QualitySession'] = {}
        
        # 全局配置
        self.default_strategy = AdaptationStrategy.BALANCED
        self.adaptation_interval = 5.0  # 秒
        self.metrics_window = 30     # 历史指标窗口大小
        
        # 统计信息
        self.global_stats = {
            "total_adaptations": 0,
            "successful_adaptations": 0,
            "upgrades": 0,
            "downgrades": 0,
            "average_network_condition": "unknown"
        }
        
        logger.info("AdaptiveQualityManager initialized")
    
    async def create_session(
        self,
        session_id: str,
        user_id: str,
        initial_quality: QualityLevel = QualityLevel.MEDIUM,
        strategy: AdaptationStrategy = None,
        **kwargs
    ) -> 'QualitySession':
        """创建质量自适应会话"""
        if session_id in self.sessions:
            raise ValidationError(f"Session {session_id} already exists")
        
        session = QualitySession(
            session_id=session_id,
            user_id=user_id,
            initial_quality=initial_quality,
            strategy=strategy or self.default_strategy,
            quality_manager=self,
            **kwargs
        )
        
        self.sessions[session_id] = session
        
        logger.info(f"Created quality session {session_id} with quality {initial_quality.value}")
        return session
    
    async def get_session(self, session_id: str) -> Optional['QualitySession']:
        """获取质量会话"""
        return self.sessions.get(session_id)
    
    async def remove_session(self, session_id: str) -> bool:
        """移除质量会话"""
        if session_id not in self.sessions:
            return False
        
        session = self.sessions[session_id]
        await session.stop()
        del self.sessions[session_id]
        
        logger.info(f"Removed quality session {session_id}")
        return True
    
    async def get_recommended_quality(
        self,
        network_metrics: NetworkMetrics,
        performance_metrics: Optional[PerformanceMetrics] = None,
        user_preference: Optional[QualityLevel] = None
    ) -> QualityLevel:
        """获取推荐质量"""
        try:
            network_condition = network_metrics.get_condition()
            
            # 基于网络状况的基础推荐
            if network_condition == NetworkCondition.VERY_POOR:
                base_quality = QualityLevel.LOW
            elif network_condition == NetworkCondition.POOR:
                base_quality = QualityLevel.LOW
            elif network_condition == NetworkCondition.FAIR:
                base_quality = QualityLevel.MEDIUM
            elif network_condition == NetworkCondition.GOOD:
                base_quality = QualityLevel.MEDIUM
            else:  # EXCELLENT
                base_quality = QualityLevel.HIGH
            
            # 考虑性能指标
            if performance_metrics:
                if performance_metrics.cpu_usage > 80:
                    base_quality = min(base_quality, QualityLevel.LOW)
                elif performance_metrics.cpu_usage > 60:
                    base_quality = min(base_quality, QualityLevel.MEDIUM)
                elif performance_metrics.cpu_usage < 30 and network_condition == NetworkCondition.EXCELLENT:
                    base_quality = QualityLevel.ULTRA
                
                if performance_metrics.buffer_health < 0.3:
                    base_quality = QualityLevel.LOW
                elif performance_metrics.underrun_count > 3:
                    base_quality = min(base_quality, QualityLevel.LOW)
            
            # 考虑用户偏好
            if user_preference:
                # 在网络条件允许的情况下，优先考虑用户偏好
                if network_condition in [NetworkCondition.GOOD, NetworkCondition.EXCELLENT]:
                    base_quality = user_preference
                elif network_condition == NetworkCondition.FAIR:
                    base_quality = min(user_preference, QualityLevel.MEDIUM)
            
            # 检查带宽是否足够
            if network_metrics.bandwidth_kbps > 0:
                base_quality = self._check_bandwidth_limitation(
                    base_quality, network_metrics.bandwidth_kbps
                )
            
            return base_quality
            
        except Exception as e:
            logger.error(f"Failed to get recommended quality: {e}")
            return QualityLevel.MEDIUM
    
    def _check_bandwidth_limitation(self, quality: QualityLevel, bandwidth_kbps: float) -> QualityLevel:
        """检查带宽限制"""
        config = self.quality_configs.get(quality)
        if not config:
            return quality
        
        # 如果带宽不足，降级质量
        if bandwidth_kbps < config.bandwidth_requirement * 0.8:  # 80%缓冲
            if quality == QualityLevel.ULTRA:
                return QualityLevel.HIGH
            elif quality == QualityLevel.HIGH:
                return QualityLevel.MEDIUM
            elif quality == QualityLevel.MEDIUM:
                return QualityLevel.LOW
        
        return quality
    
    def get_quality_config(self, quality: QualityLevel) -> Optional[QualityConfig]:
        """获取质量配置"""
        return self.quality_configs.get(quality)
    
    async def get_global_statistics(self) -> Dict[str, Any]:
        """获取全局统计信息"""
        active_sessions = len(self.sessions)
        
        # 收集所有会话的统计信息
        all_network_conditions = []
        total_upgrades = 0
        total_downgrades = 0
        
        for session in self.sessions.values():
            stats = session.get_statistics()
            all_network_conditions.append(stats["current_network_condition"])
            total_upgrades += stats["upgrade_count"]
            total_downgrades += stats["downgrade_count"]
        
        # 计算平均网络状况
        if all_network_conditions:
            condition_counts = {condition: all_network_conditions.count(condition) 
                              for condition in NetworkCondition}
            most_common = max(condition_counts, key=condition_counts.get)
            avg_network_condition = most_common.value
        else:
            avg_network_condition = "unknown"
        
        global_stats = {
            **self.global_stats,
            "active_sessions": active_sessions,
            "total_upgrades": total_upgrades,
            "total_downgrades": total_downgrades,
            "average_network_condition": avg_network_condition,
            "success_rate": (self.global_stats["successful_adaptations"] / 
                           max(1, self.global_stats["total_adaptations"])) * 100
        }
        
        return global_stats


class QualitySession:
    """质量自适应会话"""
    
    def __init__(
        self,
        session_id: str,
        user_id: str,
        quality_manager: AdaptiveQualityManager,
        initial_quality: QualityLevel = QualityLevel.MEDIUM,
        strategy: AdaptationStrategy = AdaptationStrategy.BALANCED,
        **kwargs
    ):
        self.session_id = session_id
        self.user_id = user_id
        self.quality_manager = quality_manager
        self.strategy = strategy
        self.initial_quality = initial_quality
        
        # 当前状态
        self.current_quality = initial_quality
        self.current_config = quality_manager.get_quality_config(initial_quality)
        
        # 指标收集
        self.network_metrics_history: List[NetworkMetrics] = []
        self.performance_metrics_history: List[PerformanceMetrics] = []
        self.adaptation_history: List[AdaptationHistory] = []
        
        # 控制标志
        self._running = False
        self._adaptation_task = None
        
        # 统计信息
        self.stats = {
            "upgrade_count": 0,
            "downgrade_count": 0,
            "total_adaptations": 0,
            "current_network_condition": "unknown",
            "average_bandwidth": 0.0,
            "average_latency": 0.0,
            "buffer_health_average": 0.0
        }
        
        # 回调函数
        self.quality_change_callbacks: List[callable] = []
        
        logger.info(f"QualitySession {session_id} initialized")
    
    async def start(self):
        """启动自适应质量调整"""
        if self._running:
            return
        
        self._running = True
        self._adaptation_task = asyncio.create_task(self._adaptation_loop())
        
        logger.info(f"QualitySession {self.session_id} started")
    
    async def stop(self):
        """停止自适应质量调整"""
        if not self._running:
            return
        
        self._running = False
        
        if self._adaptation_task:
            self._adaptation_task.cancel()
            try:
                await self._adaptation_task
            except asyncio.CancelledError:
                pass
        
        logger.info(f"QualitySession {self.session_id} stopped")
    
    async def update_network_metrics(self, metrics: NetworkMetrics):
        """更新网络指标"""
        self.network_metrics_history.append(metrics)
        
        # 保持历史窗口
        if len(self.network_metrics_history) > self.quality_manager.metrics_window:
            self.network_metrics_history.pop(0)
        
        # 更新统计
        self._update_network_stats()
    
    async def update_performance_metrics(self, metrics: PerformanceMetrics):
        """更新性能指标"""
        self.performance_metrics_history.append(metrics)
        
        # 保持历史窗口
        if len(self.performance_metrics_history) > self.quality_manager.metrics_window:
            self.performance_metrics_history.pop(0)
        
        # 更新统计
        self._update_performance_stats()
    
    def add_quality_change_callback(self, callback: callable):
        """添加质量变化回调"""
        self.quality_change_callbacks.append(callback)
    
    def get_current_quality(self) -> QualityLevel:
        """获取当前质量"""
        return self.current_quality
    
    def get_current_config(self) -> Optional[QualityConfig]:
        """获取当前配置"""
        return self.current_config
    
    def get_statistics(self) -> Dict[str, Any]:
        """获取统计信息"""
        return {
            **self.stats,
            "current_quality": self.current_quality.value,
            "strategy": self.strategy.value,
            "adaptation_history_count": len(self.adaptation_history),
            "network_metrics_count": len(self.network_metrics_history),
            "performance_metrics_count": len(self.performance_metrics_history)
        }
    
    async def force_quality_change(self, new_quality: QualityLevel, reason: str = "Manual"):
        """强制质量变化"""
        await self._change_quality(new_quality, reason, manual=True)
    
    async def _adaptation_loop(self):
        """自适应调整循环"""
        while self._running:
            try:
                await asyncio.sleep(self.quality_manager.adaptation_interval)
                
                if not self._running:
                    break
                
                # 执行质量自适应
                await self._perform_adaptation()
                
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Adaptation loop error in session {self.session_id}: {e}")
                await asyncio.sleep(5.0)
    
    async def _perform_adaptation(self):
        """执行质量自适应"""
        try:
            # 获取最新的网络和性能指标
            latest_network = self.network_metrics_history[-1] if self.network_metrics_history else None
            latest_performance = self.performance_metrics_history[-1] if self.performance_metrics_history else None
            
            if not latest_network:
                return
            
            # 获取推荐质量
            recommended_quality = await self.quality_manager.get_recommended_quality(
                network_metrics=latest_network,
                performance_metrics=latest_performance
            )
            
            # 检查是否需要调整质量
            if recommended_quality != self.current_quality:
                should_adapt = await self._should_adapt_quality(
                    recommended_quality, latest_network, latest_performance
                )
                
                if should_adapt:
                    await self._change_quality(
                        recommended_quality, 
                        f"Auto-adaptation based on {latest_network.get_condition().value}"
                    )
        
        except Exception as e:
            logger.error(f"Adaptation error in session {self.session_id}: {e}")
    
    async def _should_adapt_quality(
        self,
        recommended_quality: QualityLevel,
        network_metrics: NetworkMetrics,
        performance_metrics: Optional[PerformanceMetrics]
    ) -> bool:
        """判断是否应该调整质量"""
        # 根据策略判断
        if self.strategy == AdaptationStrategy.CONSERVATIVE:
            # 保守策略：需要连续3次相同推荐才调整
            if len(self.network_metrics_history) < 3:
                return False
            
            recent_networks = self.network_metrics_history[-3:]
            recent_recommendations = [
                await self.quality_manager.get_recommended_quality(nm, performance_metrics)
                for nm in recent_networks
            ]
            
            return all(r == recommended_quality for r in recent_recommendations)
        
        elif self.strategy == AdaptationStrategy.BALANCED:
            # 平衡策略：考虑网络稳定性
            if len(self.network_metrics_history) < 2:
                return False
            
            # 检查网络是否稳定
            recent_bandwidths = [nm.bandwidth_kbps for nm in self.network_metrics_history[-5:]]
            if len(recent_bandwidths) >= 3:
                bandwidth_std = statistics.stdev(recent_bandwidths[-3:])
                bandwidth_mean = statistics.mean(recent_bandwidths[-3:])
                bandwidth_cv = bandwidth_std / bandwidth_mean if bandwidth_mean > 0 else 0
                
                # 如果网络不稳定（变异系数 > 30%），降低调整频率
                if bandwidth_cv > 0.3:
                    return False
            
            return True
        
        elif self.strategy == AdaptationStrategy.AGGRESSIVE:
            # 激进策略：直接调整
            return True
        
        elif self.strategy == AdaptationStrategy.PRELOAD_FOCUSED:
            # 预加载优先策略：优先考虑缓冲区健康度
            if performance_metrics and performance_metrics.buffer_health < 0.5:
                return True
            
            return network_metrics.get_condition() in [
                NetworkCondition.VERY_POOR, NetworkCondition.POOR
            ]
        
        return False
    
    async def _change_quality(self, new_quality: QualityLevel, reason: str, manual: bool = False):
        """改变质量"""
        old_quality = self.current_quality
        old_config = self.current_config
        
        # 更新质量和配置
        self.current_quality = new_quality
        self.current_config = self.quality_manager.get_quality_config(new_quality)
        
        # 记录适应历史
        latest_network = self.network_metrics_history[-1] if self.network_metrics_history else None
        latest_performance = self.performance_metrics_history[-1] if self.performance_metrics_history else None
        
        adaptation = AdaptationHistory(
            timestamp=time.time(),
            old_quality=old_quality,
            new_quality=new_quality,
            network_condition=latest_network.get_condition() if latest_network else NetworkCondition.FAIR,
            performance_metrics=latest_performance or PerformanceMetrics(),
            reason=reason,
            success=True
        )
        
        self.adaptation_history.append(adaptation)
        
        # 更新统计
        if new_quality.value > old_quality.value:
            self.stats["upgrade_count"] += 1
        else:
            self.stats["downgrade_count"] += 1
        
        self.stats["total_adaptations"] += 1
        
        # 更新全局统计
        self.quality_manager.global_stats["total_adaptations"] += 1
        self.quality_manager.global_stats["successful_adaptations"] += 1
        
        if new_quality.value > old_quality.value:
            self.quality_manager.global_stats["upgrades"] += 1
        else:
            self.quality_manager.global_stats["downgrades"] += 1
        
        # 通知回调
        await self._notify_quality_change(old_quality, new_quality, old_config, self.current_config, reason)
        
        logger.info(f"Quality changed in session {self.session_id}: {old_quality.value} -> {new_quality.value} ({reason})")
    
    async def _notify_quality_change(
        self,
        old_quality: QualityLevel,
        new_quality: QualityLevel,
        old_config: QualityConfig,
        new_config: QualityConfig,
        reason: str
    ):
        """通知质量变化"""
        for callback in self.quality_change_callbacks:
            try:
                if asyncio.iscoroutinefunction(callback):
                    await callback(self.session_id, old_quality, new_quality, old_config, new_config, reason)
                else:
                    callback(self.session_id, old_quality, new_quality, old_config, new_config, reason)
            except Exception as e:
                logger.error(f"Quality change callback error: {e}")
    
    def _update_network_stats(self):
        """更新网络统计"""
        if not self.network_metrics_history:
            return
        
        recent_networks = self.network_metrics_history[-10:]  # 最近10个指标
        
        # 平均带宽和延迟
        self.stats["average_bandwidth"] = statistics.mean([nm.bandwidth_kbps for nm in recent_networks])
        self.stats["average_latency"] = statistics.mean([nm.latency_ms for nm in recent_networks])
        
        # 当前网络状况
        self.stats["current_network_condition"] = recent_networks[-1].get_condition().value
    
    def _update_performance_stats(self):
        """更新性能统计"""
        if not self.performance_metrics_history:
            return
        
        recent_performance = self.performance_metrics_history[-10:]  # 最近10个指标
        
        # 平均缓冲区健康度
        self.stats["buffer_health_average"] = statistics.mean([pm.buffer_health for pm in recent_performance])


# 全局自适应质量管理器实例
adaptive_quality_manager = AdaptiveQualityManager()
